#include "stdafx.h"
#include "DXCommon.h"
#include "../sgp/Rect.h"
#include "../sgp/VideoSurfaceHandle.h"
#include "../sgp/Image.h"

namespace ja2{ namespace sgp
{
	Image::Image(std::uint_least16_t Width, std::uint_least16_t Height, std::uint_least16_t Flags, std::string const &ImagePath)
		: m_Width(Width), m_Height(Height), m_Flags(Flags), m_ImageFilePath(ImagePath)
	{
	}

	std::uint_least16_t Image::width() const
	{
		return m_Width;
	}
	
	std::uint_least16_t Image::height() const
	{
		return m_Height;
	}

	VideoSurfacePtr_t Image::create_surface(IDirectDraw7Ptr pDirectDraw, VideoSurface::MemoryUsage MemUsage, TransparencyBlitFlag TransparentFlag, Rect const &SourceRect) const
	{
		// Assertions
		JA2_ASSERT(height() > 0);
		JA2_ASSERT(width() > 0);
		// If source rect isn't specified, set as whole image
		Rect const src_rect = (SourceRect.empty() ? Rect(0, 0, m_Width, m_Height) : SourceRect);
		// First do Pixel Format
		DDPIXELFORMAT pixel_format = DDPIXELFORMAT();
		pixel_format.dwSize = sizeof( pixel_format );
		pixel_format.dwFlags = DDPF_RGB;
		// BPP always 32
		pixel_format.dwRGBBitCount = 32;
		pixel_format.dwRBitMask = 0x00FF0000;
		pixel_format.dwGBitMask  = 0x0000FF00;
		pixel_format.dwBBitMask  = 0x000000FF;
		// Set surface attributes
		DDSURFACEDESC2 surface_description = DDSURFACEDESC2();
		surface_description.dwSize = sizeof(surface_description);
		surface_description.dwWidth = src_rect.width();
		surface_description.dwHeight = src_rect.height();
		surface_description.ddpfPixelFormat = pixel_format;
		surface_description.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT;
		surface_description.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
		// Do memory description, based on specified flags
		switch(MemUsage)
		{
		case VideoSurface::MU_SYSTEM:
			surface_description.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY;
			break;
			// Do nothing
		default:
			break;
		}
		// Create Surface
		IDirectDrawSurfacePtr p_surface;
		dx_attempt(pDirectDraw->CreateSurface(&surface_description, &p_surface, NULL));
		// Determine memory and other attributes of newly created surface
		p_surface->GetSurfaceDesc(&surface_description);
		// Fail if create tried for video but it's in system
		if ((MemUsage == VideoSurface::MU_VIDEO) && (surface_description.ddsCaps.dwCaps & DDSCAPS_SYSTEMMEMORY))
			JA2_DEBUG_MESSAGE(TOPIC_VIDEOSURFACE, DebugManager::DL_2, "Failed to create Video Surface in video memory");
		// Look for system memory
		if(surface_description.ddsCaps.dwCaps & DDSCAPS_SYSTEMMEMORY )
			MemUsage = VideoSurface::MU_SYSTEM;
		// Look for video memory, create backup surface
		IDirectDrawSurfacePtr p_saved_surface;
		if( surface_description.ddsCaps.dwCaps & DDSCAPS_VIDEOMEMORY )
		{
			MemUsage = VideoSurface::MU_VIDEO;
			surface_description.dwSize = sizeof(surface_description);
			surface_description.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT;
			surface_description.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY;
			surface_description.dwWidth = src_rect.width();
			surface_description.dwHeight = src_rect.height();
			surface_description.ddpfPixelFormat = pixel_format;
			// Create Surface
			dx_attempt(pDirectDraw->CreateSurface(&surface_description, &p_saved_surface, NULL));
		}
		// Blit image to surface
		switch(TransparentFlag)
		{
		case TBF_SHADOW:
			m_pImageData->blit_shadow(p_surface, src_rect);
			break;
		case TBF_TRANSPARENT:
		case TBF_DEFAULT:
			m_pImageData->blit_transparent(p_surface, src_rect);
			break;
		}
		// Return surface
		return VideoSurfacePtr_t(new VideoSurface(32, VideoSurfaceHandlePtr_t(new VideoSurfaceHandle(p_surface, p_saved_surface, 0, width(), height())), MemUsage));
	}

	void Image::set_image_data(IImageDataPtr_t &&pImageData)
	{
		m_pImageData = std::move(pImageData);
	}

	PaletteVector_t const *Image::palette() const
	{
		return m_pImageData->palette();
	}

	PaletteVector_t Image::create_palette_shaded(PaletteVector_t const &Palette, std::uint_least32_t RedScale, std::uint_least32_t GreenScale, std::uint_least32_t BlueScale, bool Mono)
	{
		// Must be valid palette
		JA2_ASSERT(Palette.size() == 256);
		// Create new palette
		PaletteVector_t out;
		out.reserve(256);
		// Create palette
		for(int i = 0; i < 256; ++i)
		{
			std::uint_least32_t r_mod, g_mod, b_mod;
			// Get palette entry
			auto const &pal_entry = Palette.at(i);
			// Mono palette
			if(Mono)
			{
				std::uint_least32_t const lumin = (pal_entry.Red * 299 / 1000) + (pal_entry.Green * 587 / 1000) + (pal_entry.Blue * 114 / 1000);
				r_mod = (RedScale * lumin) / 256;
				g_mod = (GreenScale * lumin) / 256;
				b_mod = (BlueScale * lumin) / 256;
			}
			else
			{
				r_mod = (RedScale * pal_entry.Red / 256);
				g_mod = (GreenScale * pal_entry.Green / 256);
				b_mod = (BlueScale * pal_entry.Blue / 256);
			}

			std::uint_least8_t const r = static_cast<std::uint_least8_t>(std::min(r_mod, std::uint_least32_t(255)));
			std::uint_least8_t const g = static_cast<std::uint_least8_t>(std::min(g_mod, std::uint_least32_t(255)));
			std::uint_least8_t const b = static_cast<std::uint_least8_t>(std::min(b_mod, std::uint_least32_t(255)));
			// Prevent creation of pure black color
//			usColor	= (r16&gusRedMask)|(g16&gusGreenMask)|(b16&gusBlueMask);
//x			if(r == 0 && g == 0 && b == 0)
//x				usColor=BLACK_SUBSTITUTE | gusAlphaMask;

			out.push_back(PaletteEntry(r, g, b));
		}

		return out;
	}
} /*sgp*/} /*ja2*/